import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler, LabelEncoder
import pickle
import os

# 配置
TEXT_FEATURE_COLS = ['风向']
NUMERIC_FEATURE_COLS = ['温度℃', '降水量(mm)', '风力(级)', '风速(km/h)', '风向角度(度)', '气压(hPa)',
                        '湿度(%)', '空气质量', '能见度(km)', '云量%', '露点℃', '短波辐射W/m²',
                        '直接辐射W/m²', '散射辐射W/m²', '直接正常辐照度W/m²']
TIME_STEP = 8
SCALERS_PATH = 'model/scalers.pkl'
ENCODERS_PATH = 'model/encoders.pkl'


def load_preprocessing_objects():
    """加载预处理所需的标量和编码器"""
    if not os.path.exists(SCALERS_PATH) or not os.path.exists(ENCODERS_PATH):
        raise FileNotFoundError("预处理所需的标量或编码器文件不存在")

    with open(SCALERS_PATH, 'rb') as f:
        scalers = pickle.load(f)

    with open(ENCODERS_PATH, 'rb') as f:
        encoders = pickle.load(f)

    return scalers, encoders


def preprocess_input(features):
    """
    预处理输入特征用于模型预测

    参数:
        features: 包含时间序列特征的列表，长度应为TIME_STEP
                  每个元素是一个包含所有特征的字典

    返回:
        处理后的numpy数组，形状为(1, TIME_STEP, n_features)
    """
    # 验证输入长度
    if len(features) != TIME_STEP:
        raise ValueError(f"输入特征长度应为{TIME_STEP}，实际为{len(features)}")

    # 转换为DataFrame
    df = pd.DataFrame(features)

    # 加载预处理对象
    scalers, encoders = load_preprocessing_objects()

    # 处理文本特征
    for col in TEXT_FEATURE_COLS:
        if col in df.columns:
            df[col] = df[col].fillna('未知').astype(str)
            # 处理训练集中未出现过的类别
            known_classes = set(encoders[col].classes_)
            # 使用训练集中第一个类别作为默认值
            default_class = encoders[col].classes_[0]
            df[col] = df[col].apply(lambda x: x if x in known_classes else default_class)
            try:
                df[col] = encoders[col].transform(df[col])
            except ValueError as e:
                # 如果仍然出现未见过的标签，使用默认类别
                df[col] = default_class
                df[col] = encoders[col].transform(df[col])

    # 处理数值特征，确保与训练时处理方式一致
    for col in NUMERIC_FEATURE_COLS:
        if col in df.columns:
            # 确保数据是字符串类型
            df[col] = df[col].astype(str)
            # 与训练时保持一致，先提取数字部分
            extracted = df[col].str.findall(r'(-?\d+\.?\d*)')
            df[col] = extracted.apply(lambda x: x[0] if len(x) > 0 else '0')
            # 转换为数值
            df[col] = pd.to_numeric(df[col], errors='coerce')
            # 填充缺失值 - 添加异常处理
            try:
                mean_val = scalers[col].mean_ if hasattr(scalers[col], 'mean_') else 0
                # 确保mean_val是一个标量数值
                if isinstance(mean_val, (np.ndarray, list)):
                    mean_val = float(mean_val[0]) if len(mean_val) > 0 else 0.0
                else:
                    mean_val = float(mean_val)
                df[col] = df[col].fillna(mean_val)
            except Exception as e:
                # 如果填充失败，默认使用0填充
                df[col] = df[col].fillna(0)
            # 标准化
            try:
                df[col] = scalers[col].transform(df[col].values.reshape(-1, 1))
            except Exception as e:
                # 如果标准化失败，使用0值
                df[col] = 0

    # 确保特征顺序正确
    all_features = NUMERIC_FEATURE_COLS + TEXT_FEATURE_COLS
    df = df.reindex(columns=all_features, fill_value=0)

    # 转换为模型输入格式
    input_data = df.values.reshape(1, TIME_STEP, -1)

    return input_data


def save_preprocessing_objects(scalers, encoders):
    """保存预处理所需的标量和编码器（训练时使用）"""
    with open(SCALERS_PATH, 'wb') as f:
        pickle.dump(scalers, f)

    with open(ENCODERS_PATH, 'wb') as f:
        pickle.dump(encoders, f)
